﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Media;


namespace MainGame.GameSound
{

  public interface ISoundManager<T>
  {
    void PlaySound(T sound);
    void PlaySound(T sound, float volume, float pitch, float pan);
    void PlayNextSong();
    string GetSongName();

    void StopAllSounds();
    void StopMusic();

    // The sound effects volume
    float SfxVolume { get; set; }
    bool SfxMuted { get; set; }

    // The music volume
    float MusicVolume { get; set; }
    bool MusicMuted { get; set; }
  }

  public abstract class SoundManager<T> : Microsoft.Xna.Framework.GameComponent, MainGame.GameSound.ISoundManager<T>
  {
    // Sound array
    protected SoundEffect[] soundLibrary;
    protected const int MaxSounds = 32;

    // Played sounds list
    protected SoundEffectInstance[] playingSounds;

    // The sound effects volume
    public float SfxVolume { get; set; }
    public bool SfxMuted { get; set; }

    // Music library
    protected MediaLibrary mediaLibrary;

    // The music volume
    public float MusicVolume { get { return MediaPlayer.Volume; } set { MediaPlayer.Volume = value; } }
    public bool MusicMuted { get; set; }

    public SoundManager(Game game)
      : base(game) 
    {
      game.Services.AddService(typeof(ISoundManager<T>), this);
    }

    protected override void Dispose(bool disposing)
    {
      Game.Services.RemoveService(typeof(ISoundManager<T>));
      base.Dispose(disposing);
    }

    public override void Initialize()
    {
      //soundLibrary = new SoundEffect[MaxSounds];
      mediaLibrary = new MediaLibrary();
      //MusicVolume = 0;
      playingSounds = new SoundEffectInstance[MaxSounds];

      // Load the audio configuration for the current user
      var context = Game.GetContext();
      SfxMuted = context.currentConfig.sfxMuted;
      SfxVolume = context.currentConfig.sfxVolume;
      MusicMuted = context.currentConfig.musicMuted;
      MusicVolume = context.currentConfig.musicVolume;

      base.Initialize();
    }

    public override void Update(GameTime gameTime)
    {
      // Keeps the sound instances collection updated
      for (int i = 0; i < playingSounds.Length; ++i)
      {
        if (playingSounds[i] != null && playingSounds[i].State == SoundState.Paused)
        {
          playingSounds[i].Dispose();
          playingSounds[i] = null;
        }
      }

      // Check if it's time to change song
      //if (MediaPlayer.State != MediaState.Playing && !MusicMuted)
      //  PlayNextSong();

      base.Update(gameTime);
    }


    public abstract void PlaySound(T sound);

    public abstract void PlaySound(T sound, float volume, float pitch, float pan);

    /// <summary>
    /// Plays another song chosen randomly in the library
    /// </summary>
    public void PlayNextSong()
    {
      if (MusicMuted) return;
      //if (MediaPlayer.State == MediaState.Paused)
      //  MediaPlayer.Resume();

      if (MediaPlayer.Queue.ActiveSong != null)
        MediaPlayer.Queue.ActiveSong.Dispose();

      if (mediaLibrary.Albums.Count > 0)
      {
        int albumNumber = (new Random()).Next(0, mediaLibrary.Albums.Count);
        int songNumber = (new Random()).Next(0, mediaLibrary.Albums[albumNumber].Songs.Count);
        MediaPlayer.Play(mediaLibrary.Albums[albumNumber].Songs[songNumber]);
      }
    }

    /// <summary>
    /// Stops all playing sounds
    /// </summary>
    public void StopAllSounds()
    {
      for (int i = 0; i < playingSounds.Length; ++i)
      {
        if (playingSounds[i] != null)
        {
          playingSounds[i].Stop();
          playingSounds[i].Dispose();
          playingSounds[i] = null;
        }
      }
    }

    /// <summary>
    /// Stops the playing music
    /// </summary>
    public void StopMusic()
    {
      if (MediaPlayer.State == MediaState.Playing)
        MediaPlayer.Stop();
        //MediaPlayer.Pause();
    }

    /// <summary>
    /// Adds a new played sound to the playing sounds list
    /// </summary>
    /// <param name="sndInst">The sound instance to add to the list</param>
    protected void AddToPlayingQueue(SoundEffectInstance sndInst)
    {
      int nextAvailableIdx = GetAvailableSoundIndex();
      if (nextAvailableIdx != -1)
        playingSounds[nextAvailableIdx] = sndInst;
    }

    /// <summary>
    /// Gets the next available index in the sound instances collection
    /// </summary>
    /// <returns>The first available position in the sound instances collection</returns>
    private int GetAvailableSoundIndex()
    {
      for (int i = 0; i < MaxSounds; ++i)
      {
        if (playingSounds[i] == null)
          return i;
      }
      return -1;
    }

    /// <summary>
    /// Returns the artist and title of the song currently playing
    /// </summary>
    /// <returns>A string containing artist and title of the song</returns>
    public string GetSongName()
    {
      if (MediaPlayer.State == MediaState.Playing)
      {
        Song activeSong = MediaPlayer.Queue.ActiveSong;
        return activeSong.Artist + " - " + activeSong.Name;
      }
      return "";
    }

  }
}
